home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / cg_view.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  19.9 KB  |  777 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // cg_view.c -- setup all the parameters (position, angle, etc)
  4. // for a 3D rendering
  5. #include "cg_local.h"
  6.  
  7.  
  8. /*
  9. =============================================================================
  10.  
  11.   MODEL TESTING
  12.  
  13. The viewthing and gun positioning tools from Q2 have been integrated and
  14. enhanced into a single model testing facility.
  15.  
  16. Model viewing can begin with either "testmodel <modelname>" or "testgun <modelname>".
  17.  
  18. The names must be the full pathname after the basedir, like 
  19. "models/weapons/v_launch/tris.md3" or "players/male/tris.md3"
  20.  
  21. Testmodel will create a fake entity 100 units in front of the current view
  22. position, directly facing the viewer.  It will remain immobile, so you can
  23. move around it to view it from different angles.
  24.  
  25. Testgun will cause the model to follow the player around and supress the real
  26. view weapon model.  The default frame 0 of most guns is completely off screen,
  27. so you will probably have to cycle a couple frames to see it.
  28.  
  29. "nextframe", "prevframe", "nextskin", and "prevskin" commands will change the
  30. frame or skin of the testmodel.  These are bound to F5, F6, F7, and F8 in
  31. q3default.cfg.
  32.  
  33. If a gun is being tested, the "gun_x", "gun_y", and "gun_z" variables will let
  34. you adjust the positioning.
  35.  
  36. Note that none of the model testing features update while the game is paused, so
  37. it may be convenient to test with deathmatch set to 1 so that bringing down the
  38. console doesn't pause the game.
  39.  
  40. =============================================================================
  41. */
  42.  
  43. /*
  44. =================
  45. CG_TestModel_f
  46.  
  47. Creates an entity in front of the current position, which
  48. can then be moved around
  49. =================
  50. */
  51. void CG_TestModel_f (void) {
  52.     vec3_t        angles;
  53.  
  54.     memset( &cg.testModelEntity, 0, sizeof(cg.testModelEntity) );
  55.     if ( trap_Argc() < 2 ) {
  56.         return;
  57.     }
  58.  
  59.     Q_strncpyz (cg.testModelName, CG_Argv( 1 ), MAX_QPATH );
  60.     cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
  61.  
  62.     if ( trap_Argc() == 3 ) {
  63.         cg.testModelEntity.backlerp = atof( CG_Argv( 2 ) );
  64.         cg.testModelEntity.frame = 1;
  65.         cg.testModelEntity.oldframe = 0;
  66.     }
  67.     if (! cg.testModelEntity.hModel ) {
  68.         CG_Printf( "Can't register model\n" );
  69.         return;
  70.     }
  71.  
  72.     VectorMA( cg.refdef.vieworg, 100, cg.refdef.viewaxis[0], cg.testModelEntity.origin );
  73.  
  74.     angles[PITCH] = 0;
  75.     angles[YAW] = 180 + cg.refdefViewAngles[1];
  76.     angles[ROLL] = 0;
  77.  
  78.     AnglesToAxis( angles, cg.testModelEntity.axis );
  79.     cg.testGun = qfalse;
  80. }
  81.  
  82. /*
  83. =================
  84. CG_TestGun_f
  85.  
  86. Replaces the current view weapon with the given model
  87. =================
  88. */
  89. void CG_TestGun_f (void) {
  90.     CG_TestModel_f();
  91.     cg.testGun = qtrue;
  92.     cg.testModelEntity.renderfx = RF_MINLIGHT | RF_DEPTHHACK | RF_FIRST_PERSON;
  93. }
  94.  
  95.  
  96. void CG_TestModelNextFrame_f (void) {
  97.     cg.testModelEntity.frame++;
  98.     CG_Printf( "frame %i\n", cg.testModelEntity.frame );
  99. }
  100.  
  101. void CG_TestModelPrevFrame_f (void) {
  102.     cg.testModelEntity.frame--;
  103.     if ( cg.testModelEntity.frame < 0 ) {
  104.         cg.testModelEntity.frame = 0;
  105.     }
  106.     CG_Printf( "frame %i\n", cg.testModelEntity.frame );
  107. }
  108.  
  109. void CG_TestModelNextSkin_f (void) {
  110.     cg.testModelEntity.skinNum++;
  111.     CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
  112. }
  113.  
  114. void CG_TestModelPrevSkin_f (void) {
  115.     cg.testModelEntity.skinNum--;
  116.     if ( cg.testModelEntity.skinNum < 0 ) {
  117.         cg.testModelEntity.skinNum = 0;
  118.     }
  119.     CG_Printf( "skin %i\n", cg.testModelEntity.skinNum );
  120. }
  121.  
  122. static void CG_AddTestModel (void) {
  123.     int        i;
  124.  
  125.     // re-register the model, because the level may have changed
  126.     cg.testModelEntity.hModel = trap_R_RegisterModel( cg.testModelName );
  127.     if (! cg.testModelEntity.hModel ) {
  128.         CG_Printf ("Can't register model\n");
  129.         return;
  130.     }
  131.  
  132.     // if testing a gun, set the origin reletive to the view origin
  133.     if ( cg.testGun ) {
  134.         VectorCopy( cg.refdef.vieworg, cg.testModelEntity.origin );
  135.         VectorCopy( cg.refdef.viewaxis[0], cg.testModelEntity.axis[0] );
  136.         VectorCopy( cg.refdef.viewaxis[1], cg.testModelEntity.axis[1] );
  137.         VectorCopy( cg.refdef.viewaxis[2], cg.testModelEntity.axis[2] );
  138.  
  139.         // allow the position to be adjusted
  140.         for (i=0 ; i<3 ; i++) {
  141.             cg.testModelEntity.origin[i] += cg.refdef.viewaxis[0][i] * cg_gun_x.value;
  142.             cg.testModelEntity.origin[i] += cg.refdef.viewaxis[1][i] * cg_gun_y.value;
  143.             cg.testModelEntity.origin[i] += cg.refdef.viewaxis[2][i] * cg_gun_z.value;
  144.         }
  145.     }
  146.  
  147.     trap_R_AddRefEntityToScene( &cg.testModelEntity );
  148. }
  149.  
  150.  
  151.  
  152. //============================================================================
  153.  
  154.  
  155. /*
  156. =================
  157. CG_CalcVrect
  158.  
  159. Sets the coordinates of the rendered window
  160. =================
  161. */
  162. static void CG_CalcVrect (void) {
  163.     int        size;
  164.  
  165.     // the intermission should allways be full screen
  166.     if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
  167.         size = 100;
  168.     } else {
  169.         // bound normal viewsize
  170.         if (cg_viewsize.integer < 30) {
  171.             trap_Cvar_Set ("cg_viewsize","30");
  172.             size = 30;
  173.         } else if (cg_viewsize.integer > 100) {
  174.             trap_Cvar_Set ("cg_viewsize","100");
  175.             size = 100;
  176.         } else {
  177.             size = cg_viewsize.integer;
  178.         }
  179.  
  180.     }
  181.     cg.refdef.width = cgs.glconfig.vidWidth*size/100;
  182.     cg.refdef.width &= ~1;
  183.  
  184.     cg.refdef.height = cgs.glconfig.vidHeight*size/100;
  185.     cg.refdef.height &= ~1;
  186.  
  187.     cg.refdef.x = (cgs.glconfig.vidWidth - cg.refdef.width)/2;
  188.     cg.refdef.y = (cgs.glconfig.vidHeight - cg.refdef.height)/2;
  189. }
  190.  
  191. //==============================================================================
  192.  
  193.  
  194. /*
  195. ===============
  196. CG_OffsetThirdPersonView
  197.  
  198. ===============
  199. */
  200. #define    FOCUS_DISTANCE    512
  201. static void CG_OffsetThirdPersonView( void ) {
  202.     vec3_t        forward, right, up;
  203.     vec3_t        view;
  204.     vec3_t        focusAngles;
  205.     trace_t        trace;
  206.     static vec3_t    mins = { -4, -4, -4 };
  207.     static vec3_t    maxs = { 4, 4, 4 };
  208.     vec3_t        focusPoint;
  209.     float        focusDist;
  210.     float        forwardScale, sideScale;
  211.  
  212.     cg.refdef.vieworg[2] += cg.predictedPlayerState.viewheight;
  213.  
  214.     VectorCopy( cg.refdefViewAngles, focusAngles );
  215.  
  216.     // if dead, look at killer
  217.     if ( cg.predictedPlayerState.stats[STAT_HEALTH] <= 0 ) {
  218.         focusAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
  219.         cg.refdefViewAngles[YAW] = cg.predictedPlayerState.stats[STAT_DEAD_YAW];
  220.     }
  221.  
  222.     if ( focusAngles[PITCH] > 45 ) {
  223.         focusAngles[PITCH] = 45;        // don't go too far overhead
  224.     }
  225.     AngleVectors( focusAngles, forward, NULL, NULL );
  226.  
  227.     VectorMA( cg.refdef.vieworg, FOCUS_DISTANCE, forward, focusPoint );
  228.  
  229.     VectorCopy( cg.refdef.vieworg, view );
  230.  
  231.     view[2] += 8;
  232.  
  233.     cg.refdefViewAngles[PITCH] *= 0.5;
  234.  
  235.     AngleVectors( cg.refdefViewAngles, forward, right, up );
  236.  
  237.     forwardScale = cos( cg_thirdPersonAngle.value / 180 * M_PI );
  238.     sideScale = sin( cg_thirdPersonAngle.value / 180 * M_PI );
  239.     VectorMA( view, -cg_thirdPersonRange.value * forwardScale, forward, view );
  240.     VectorMA( view, -cg_thirdPersonRange.value * sideScale, right, view );
  241.  
  242.     // trace a ray from the origin to the viewpoint to make sure the view isn't
  243.     // in a solid block.  Use an 8 by 8 block to prevent the view from near clipping anything
  244.  
  245.     CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
  246.  
  247.     if ( trace.fraction != 1.0 ) {
  248.         VectorCopy( trace.endpos, view );
  249.         view[2] += (1.0 - trace.fraction) * 32;
  250.         // try another trace to this position, because a tunnel may have the ceiling
  251.         // close enogh that this is poking out
  252.  
  253.         CG_Trace( &trace, cg.refdef.vieworg, mins, maxs, view, cg.predictedPlayerState.clientNum, MASK_SOLID );
  254.         VectorCopy( trace.endpos, view );
  255.     }
  256.  
  257.  
  258.     VectorCopy( view, cg.refdef.vieworg );
  259.  
  260.     // select pitch to look at focus point from vieword
  261.     VectorSubtract( focusPoint, cg.refdef.vieworg, focusPoint );
  262.     focusDist = sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
  263.     if ( focusDist < 1 ) {
  264.         focusDist = 1;    // should never happen
  265.     }
  266.     cg.refdefViewAngles[PITCH] = -180 / M_PI * atan2( focusPoint[2], focusDist );
  267.     cg.refdefViewAngles[YAW] -= cg_thirdPersonAngle.value;
  268. }
  269.  
  270.  
  271. // this causes a compiler bug on mac MrC compiler
  272. static void CG_StepOffset( void ) {
  273.     int        timeDelta;
  274.     
  275.     // smooth out stair climbing
  276.     timeDelta = cg.time - cg.stepTime;
  277.     if ( timeDelta < STEP_TIME ) {
  278.         cg.refdef.vieworg[2] -= cg.stepChange 
  279.             * (STEP_TIME - timeDelta) / STEP_TIME;
  280.     }
  281. }
  282.  
  283. /*
  284. ===============
  285. CG_OffsetFirstPersonView
  286.  
  287. ===============
  288. */
  289. static void CG_OffsetFirstPersonView( void ) {
  290.     float            *origin;
  291.     float            *angles;
  292.     float            bob;
  293.     float            ratio;
  294.     float            delta;
  295.     float            speed;
  296.     float            f;
  297.     vec3_t            predictedVelocity;
  298.     int                timeDelta;
  299.     
  300.     if ( cg.snap->ps.pm_type == PM_INTERMISSION ) {
  301.         return;
  302.     }
  303.  
  304.     origin = cg.refdef.vieworg;
  305.     angles = cg.refdefViewAngles;
  306.  
  307.     // if dead, fix the angle and don't add any kick
  308.     if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) {
  309.         angles[ROLL] = 40;
  310.         angles[PITCH] = -15;
  311.         angles[YAW] = cg.snap->ps.stats[STAT_DEAD_YAW];
  312.         origin[2] += cg.predictedPlayerState.viewheight;
  313.         return;
  314.     }
  315.  
  316.     // add angles based on weapon kick
  317.     VectorAdd (angles, cg.kick_angles, angles);
  318.  
  319.     // add angles based on damage kick
  320.     if ( cg.damageTime ) {
  321.         ratio = cg.time - cg.damageTime;
  322.         if ( ratio < DAMAGE_DEFLECT_TIME ) {
  323.             ratio /= DAMAGE_DEFLECT_TIME;
  324.             angles[PITCH] += ratio * cg.v_dmg_pitch;
  325.             angles[ROLL] += ratio * cg.v_dmg_roll;
  326.         } else {
  327.             ratio = 1.0 - ( ratio - DAMAGE_DEFLECT_TIME ) / DAMAGE_RETURN_TIME;
  328.             if ( ratio > 0 ) {
  329.                 angles[PITCH] += ratio * cg.v_dmg_pitch;
  330.                 angles[ROLL] += ratio * cg.v_dmg_roll;
  331.             }
  332.         }
  333.     }
  334.  
  335.     // add pitch based on fall kick
  336. #if 0
  337.     ratio = ( cg.time - cg.landTime) / FALL_TIME;
  338.     if (ratio < 0)
  339.         ratio = 0;
  340.     angles[PITCH] += ratio * cg.fall_value;
  341. #endif
  342.  
  343.     // add angles based on velocity
  344.     VectorCopy( cg.predictedPlayerState.velocity, predictedVelocity );
  345.  
  346.     delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[0]);
  347.     angles[PITCH] += delta * cg_runpitch.value;
  348.     
  349.     delta = DotProduct ( predictedVelocity, cg.refdef.viewaxis[1]);
  350.     angles[ROLL] -= delta * cg_runroll.value;
  351.  
  352.     // add angles based on bob
  353.  
  354.     // make sure the bob is visible even at low speeds
  355.     speed = cg.xyspeed > 200 ? cg.xyspeed : 200;
  356.  
  357.     delta = cg.bobfracsin * cg_bobpitch.value * speed;
  358.     if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
  359.         delta *= 3;        // crouching
  360.     angles[PITCH] += delta;
  361.     delta = cg.bobfracsin * cg_bobroll.value * speed;
  362.     if (cg.predictedPlayerState.pm_flags & PMF_DUCKED)
  363.         delta *= 3;        // crouching accentuates roll
  364.     if (cg.bobcycle & 1)
  365.         delta = -delta;
  366.     angles[ROLL] += delta;
  367.  
  368. //===================================
  369.  
  370.     // add view height
  371.     origin[2] += cg.predictedPlayerState.viewheight;
  372.  
  373.     // smooth out duck height changes
  374.     timeDelta = cg.time - cg.duckTime;
  375.     if ( timeDelta < DUCK_TIME) {
  376.         cg.refdef.vieworg[2] -= cg.duckChange 
  377.             * (DUCK_TIME - timeDelta) / DUCK_TIME;
  378.     }
  379.  
  380.     // add bob height
  381.     bob = cg.bobfracsin * cg.xyspeed * cg_bobup.value;
  382.     if (bob > 6) {
  383.         bob = 6;
  384.     }
  385.  
  386.     origin[2] += bob;
  387.  
  388.  
  389.     // add fall height
  390.     delta = cg.time - cg.landTime;
  391.     if ( delta < LAND_DEFLECT_TIME ) {
  392.         f = delta / LAND_DEFLECT_TIME;
  393.         cg.refdef.vieworg[2] += cg.landChange * f;
  394.     } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
  395.         delta -= LAND_DEFLECT_TIME;
  396.         f = 1.0 - ( delta / LAND_RETURN_TIME );
  397.         cg.refdef.vieworg[2] += cg.landChange * f;
  398.     }
  399.  
  400.     // add step offset
  401.     CG_StepOffset();
  402.  
  403.     // add kick offset
  404.  
  405.     VectorAdd (origin, cg.kick_origin, origin);
  406.  
  407.     // pivot the eye based on a neck length
  408. #if 0
  409.     {
  410. #define    NECK_LENGTH        8
  411.     vec3_t            forward, up;
  412.  
  413.     cg.refdef.vieworg[2] -= NECK_LENGTH;
  414.     AngleVectors( cg.refdefViewAngles, forward, NULL, up );
  415.     VectorMA( cg.refdef.vieworg, 3, forward, cg.refdef.vieworg );
  416.     VectorMA( cg.refdef.vieworg, NECK_LENGTH, up, cg.refdef.vieworg );
  417.     }
  418. #endif
  419. }
  420.  
  421. //======================================================================
  422.  
  423. void CG_ZoomDown_f( void ) { 
  424.     if ( cg.zoomed ) {
  425.         return;
  426.     }
  427.     cg.zoomed = qtrue;
  428.     cg.zoomTime = cg.time;
  429. }
  430.  
  431. void CG_ZoomUp_f( void ) { 
  432.     if ( !cg.zoomed ) {
  433.         return;
  434.     }
  435.     cg.zoomed = qfalse;
  436.     cg.zoomTime = cg.time;
  437. }
  438.  
  439.  
  440. /*
  441. ====================
  442. CG_CalcFov
  443.  
  444. Fixed fov at intermissions, otherwise account for fov variable and zooms.
  445. ====================
  446. */
  447. #define    WAVE_AMPLITUDE    1
  448. #define    WAVE_FREQUENCY    0.4
  449.  
  450. static int CG_CalcFov( void ) {
  451.     float    x;
  452.     float    phase;
  453.     float    v;
  454.     int        contents;
  455.     float    fov_x, fov_y;
  456.     float    zoomFov;
  457.     float    f;
  458.     int        inwater;
  459.  
  460.     if ( cg.predictedPlayerState.pm_type == PM_INTERMISSION ) {
  461.         // if in intermission, use a fixed value
  462.         fov_x = 90;
  463.     } else {
  464.         // user selectable
  465.         if ( cgs.dmflags & DF_FIXED_FOV ) {
  466.             // dmflag to prevent wide fov for all clients
  467.             fov_x = 90;
  468.         } else {
  469.             fov_x = cg_fov.value;
  470.             if ( fov_x < 1 ) {
  471.                 fov_x = 1;
  472.             } else if ( fov_x > 160 ) {
  473.                 fov_x = 160;
  474.             }
  475.         }
  476.  
  477.         // account for zooms
  478.         zoomFov = cg_zoomFov.value;
  479.         if ( zoomFov < 1 ) {
  480.             zoomFov = 1;
  481.         } else if ( zoomFov > 160 ) {
  482.             zoomFov = 160;
  483.         }
  484.  
  485.         if ( cg.zoomed ) {
  486.             f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
  487.             if ( f > 1.0 ) {
  488.                 fov_x = zoomFov;
  489.             } else {
  490.                 fov_x = fov_x + f * ( zoomFov - fov_x );
  491.             }
  492.         } else {
  493.             f = ( cg.time - cg.zoomTime ) / (float)ZOOM_TIME;
  494.             if ( f > 1.0 ) {
  495.                 fov_x = fov_x;
  496.             } else {
  497.                 fov_x = zoomFov + f * ( fov_x - zoomFov );
  498.             }
  499.         }
  500.     }
  501.  
  502.     x = cg.refdef.width / tan( fov_x / 360 * M_PI );
  503.     fov_y = atan2( cg.refdef.height, x );
  504.     fov_y = fov_y * 360 / M_PI;
  505.  
  506.     // warp if underwater
  507.     contents = CG_PointContents( cg.refdef.vieworg, -1 );
  508.     if ( contents & ( CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA ) ){
  509.         phase = cg.time / 1000.0 * WAVE_FREQUENCY * M_PI * 2;
  510.         v = WAVE_AMPLITUDE * sin( phase );
  511.         fov_x += v;
  512.         fov_y -= v;
  513.         inwater = qtrue;
  514.     }
  515.     else {
  516.         inwater = qfalse;
  517.     }
  518.  
  519.  
  520.     // set it
  521.     cg.refdef.fov_x = fov_x;
  522.     cg.refdef.fov_y = fov_y;
  523.  
  524.     if ( !cg.zoomed ) {
  525.         cg.zoomSensitivity = 1;
  526.     } else {
  527.         cg.zoomSensitivity = cg.refdef.fov_y / 75.0;
  528.     }
  529.  
  530.     return inwater;
  531. }
  532.  
  533.  
  534.  
  535. /*
  536. ===============
  537. CG_DamageBlendBlob
  538.  
  539. ===============
  540. */
  541. static void CG_DamageBlendBlob( void ) {
  542.     int            t;
  543.     int            maxTime;
  544.     refEntity_t        ent;
  545.  
  546.     if ( !cg.damageValue ) {
  547.         return;
  548.     }
  549.  
  550.     // ragePro systems can't fade blends, so don't obscure the screen
  551.     if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) {
  552.         return;
  553.     }
  554.  
  555.     maxTime = DAMAGE_TIME;
  556.     t = cg.time - cg.damageTime;
  557.     if ( t <= 0 || t >= maxTime ) {
  558.         return;
  559.     }
  560.  
  561.  
  562.     memset( &ent, 0, sizeof( ent ) );
  563.     ent.reType = RT_SPRITE;
  564.     ent.renderfx = RF_FIRST_PERSON;
  565.  
  566.     VectorMA( cg.refdef.vieworg, 8, cg.refdef.viewaxis[0], ent.origin );
  567.     VectorMA( ent.origin, cg.damageX * -8, cg.refdef.viewaxis[1], ent.origin );
  568.     VectorMA( ent.origin, cg.damageY * 8, cg.refdef.viewaxis[2], ent.origin );
  569.  
  570.     ent.radius = cg.damageValue * 3;
  571.     ent.customShader = cgs.media.viewBloodShader;
  572.     ent.shaderRGBA[0] = 255;
  573.     ent.shaderRGBA[1] = 255;
  574.     ent.shaderRGBA[2] = 255;
  575.     ent.shaderRGBA[3] = 200 * ( 1.0 - ((float)t / maxTime) );
  576.     trap_R_AddRefEntityToScene( &ent );
  577. }
  578.  
  579.  
  580. /*
  581. ===============
  582. CG_CalcViewValues
  583.  
  584. Sets cg.refdef view values
  585. ===============
  586. */
  587. static int CG_CalcViewValues( void ) {
  588.     playerState_t    *ps;
  589.  
  590.     memset( &cg.refdef, 0, sizeof( cg.refdef ) );
  591.  
  592.     // strings for in game rendering
  593.     // Q_strncpyz( cg.refdef.text[0], "Park Ranger", sizeof(cg.refdef.text[0]) );
  594.     // Q_strncpyz( cg.refdef.text[1], "19", sizeof(cg.refdef.text[1]) );
  595.  
  596.     // calculate size of 3D view
  597.     CG_CalcVrect();
  598.  
  599.     ps = &cg.predictedPlayerState;
  600.  
  601.     // intermission view
  602.     if ( ps->pm_type == PM_INTERMISSION ) {
  603.         VectorCopy( ps->origin, cg.refdef.vieworg );
  604.         VectorCopy( ps->viewangles, cg.refdefViewAngles );
  605.         AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
  606.         return CG_CalcFov();
  607.     }
  608.  
  609.     cg.bobcycle = ( ps->bobCycle & 128 ) >> 7;
  610.     cg.bobfracsin = fabs( sin( ( ps->bobCycle & 127 ) / 127.0 * M_PI ) );
  611.     cg.xyspeed = sqrt( ps->velocity[0] * ps->velocity[0] +
  612.         ps->velocity[1] * ps->velocity[1] );
  613.  
  614.  
  615.     VectorCopy( ps->origin, cg.refdef.vieworg );
  616.     VectorCopy( ps->viewangles, cg.refdefViewAngles );
  617.  
  618.     // add error decay
  619.     if ( cg_errorDecay.value > 0 ) {
  620.         int        t;
  621.         float    f;
  622.  
  623.         t = cg.time - cg.predictedErrorTime;
  624.         f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
  625.         if ( f > 0 && f < 1 ) {
  626.             VectorMA( cg.refdef.vieworg, f, cg.predictedError, cg.refdef.vieworg );
  627.         } else {
  628.             cg.predictedErrorTime = 0;
  629.         }
  630.     }
  631.  
  632.     if ( cg.renderingThirdPerson ) {
  633.         // back away from character
  634.         CG_OffsetThirdPersonView();
  635.     } else {
  636.         // offset for local bobbing and kicks
  637.         CG_OffsetFirstPersonView();
  638.     }
  639.  
  640.     // position eye reletive to origin
  641.     AnglesToAxis( cg.refdefViewAngles, cg.refdef.viewaxis );
  642.  
  643.     if ( cg.hyperspace ) {
  644.         cg.refdef.rdflags |= RDF_NOWORLDMODEL | RDF_HYPERSPACE;
  645.     }
  646.  
  647.     // field of view
  648.     return CG_CalcFov();
  649. }
  650.  
  651.  
  652. /*
  653. =====================
  654. CG_PowerupTimerSounds
  655. =====================
  656. */
  657. static void CG_PowerupTimerSounds( void ) {
  658.     int        i;
  659.     int        t;
  660.  
  661.     // powerup timers going away
  662.     for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
  663.         t = cg.snap->ps.powerups[i];
  664.         if ( t <= cg.time ) {
  665.             continue;
  666.         }
  667.         if ( t - cg.time >= POWERUP_BLINKS * POWERUP_BLINK_TIME ) {
  668.             continue;
  669.         }
  670.         if ( ( t - cg.time ) / POWERUP_BLINK_TIME != ( t - cg.oldTime ) / POWERUP_BLINK_TIME ) {
  671.             trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_ITEM, cgs.media.wearOffSound );
  672.         }
  673.     }
  674. }
  675.  
  676. //=========================================================================
  677.  
  678. /*
  679. =================
  680. CG_DrawActiveFrame
  681.  
  682. Generates and draws a game scene and status information at the given time.
  683. =================
  684. */
  685. void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demoPlayback ) {
  686.     int        inwater;
  687.  
  688.     cg.time = serverTime;
  689.     cg.demoPlayback = demoPlayback;
  690.  
  691.     // update cvars
  692.     CG_UpdateCvars();
  693.  
  694.     // if we are only updating the screen as a loading
  695.     // pacifier, don't even try to read snapshots
  696.     if ( cg.infoScreenText[0] != 0 ) {
  697.         CG_DrawInformation();
  698.         return;
  699.     }
  700.  
  701.     // any looped sounds will be respecified as entities
  702.     // are added to the render list
  703.     trap_S_ClearLoopingSounds();
  704.  
  705.     // clear all the render lists
  706.     trap_R_ClearScene();
  707.  
  708.     // set up cg.snap and possibly cg.nextSnap
  709.     CG_ProcessSnapshots();
  710.  
  711.     // if we haven't received any snapshots yet, all
  712.     // we can draw is the information screen
  713.     if ( !cg.snap || ( cg.snap->snapFlags & SNAPFLAG_NOT_ACTIVE ) ) {
  714.         CG_DrawInformation();
  715.         return;
  716.     }
  717.  
  718.     // let the client system know what our weapon and zoom settings are
  719.     trap_SetUserCmdValue( cg.weaponSelect, cg.zoomSensitivity );
  720.  
  721.     // this counter will be bumped for every valid scene we generate
  722.     cg.clientFrame++;
  723.  
  724.     // update cg.predictedPlayerState
  725.     CG_PredictPlayerState();
  726.  
  727.     // decide on third person view
  728.     cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0);
  729.  
  730.     // build cg.refdef
  731.     inwater = CG_CalcViewValues();
  732.  
  733.     // first person blend blobs, done after AnglesToAxis
  734.     if ( !cg.renderingThirdPerson ) {
  735.         CG_DamageBlendBlob();
  736.     }
  737.  
  738.     // build the render lists
  739.     if ( !cg.hyperspace ) {
  740.         CG_AddPacketEntities();            // adter calcViewValues, so predicted player state is correct
  741.         CG_AddMarks();
  742.         CG_AddLocalEntities();
  743.     }
  744.     CG_AddViewWeapon( &cg.predictedPlayerState );
  745.  
  746.     // finish up the rest of the refdef
  747.     if ( cg.testModelEntity.hModel ) {
  748.         CG_AddTestModel();
  749.     }
  750.     cg.refdef.time = cg.time;
  751.     memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
  752.  
  753.     // update audio positions
  754.     trap_S_Respatialize( cg.snap->ps.clientNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater );
  755.  
  756.     // warning sounds when powerup is wearing off
  757.     CG_PowerupTimerSounds();
  758.  
  759.     // make sure the lagometerSample and frame timing isn't done twice when in stereo
  760.     if ( stereoView != STEREO_RIGHT ) {
  761.         cg.frametime = cg.time - cg.oldTime;
  762.         if ( cg.frametime < 0 ) {
  763.             cg.frametime = 0;
  764.         }
  765.         cg.oldTime = cg.time;
  766.         CG_AddLagometerFrameInfo();
  767.     }
  768.  
  769.     // actually issue the rendering calls
  770.     CG_DrawActive( stereoView );
  771.  
  772.     if ( cg_stats.integer ) {
  773.         CG_Printf( "cg.clientFrame:%i\n", cg.clientFrame );
  774.     }
  775. }
  776.  
  777.